package com.pocket.demo.core.initializing;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.pocket.demo.business.dao.HotelMapper;
import com.pocket.demo.business.po.Hotel;
import jakarta.annotation.Resource;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 【demo-redis-3.1】
 * @author zhaozhile
 */
@Order(2)
@Component
public class SyncHotelBloomFilter implements PocketInitialize {

    private static final Logger LOGGER = LoggerFactory.getLogger(SyncHotelBloomFilter.class);
    // 要保持锁名称、布隆过滤器名称和其他key的唯一性，不然会报错：org.redisson.client.RedisException: WRONGTYPE Operation against a key holding the wrong kind of value script:
    public static final String BLOOM_FILTER_LOCK = "hotelBloomLock";
    public static final String BLOOM_FILTER_NAME = "hotelBloomFilter";

    @Resource
    RedissonClient redissonClient;
    @Resource private HotelMapper hotelMapper;

    @Override
    public void initialize(ApplicationReadyEvent event){

        //【demo-redis-4】
        RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock(BLOOM_FILTER_LOCK);
        RLock rLock = rReadWriteLock.writeLock();
        // 获取不到锁，说明已经有实例在初始化，直接跳过
        if (rLock.tryLock()) {
            try {
                // 判断是否已被初始化过
                RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(BLOOM_FILTER_NAME);

                //初始化布隆过滤器：预计元素为1000000L,误差率为3%
                boolean initSuccess = bloomFilter.tryInit(1000000L,0.03);
                if (!initSuccess){
                    LOGGER.debug("BloomFilter has been initialized");
                    return;
                }

                // 初始化 Hotel 业务数据
                hotelMapper.selectList(new LambdaQueryWrapper<Hotel>().select(Hotel::getId), resultContext -> {
                    bloomFilter.add(resultContext.getResultObject().getId().toString());
                });
                LOGGER.debug("Sync hotel data to RBloomFilter, total count : {}", bloomFilter.count());
            } catch (Exception e){
                LOGGER.error("Sync hotel data to RBloomFilter error", e);
            }finally {
                rLock.unlock();
            }
        }
    }
}
